package com.zy.view.sort;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.os.Build;
import android.support.annotation.MainThread;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.LinearInterpolator;

import com.zy.utils.CanvasUtils;
import com.zy.utils.DataBuildUtils;
import com.zy.utils.data.AssistPoint;
import com.zy.utils.data.Point;
import com.zy.utils.data.SubScriptPoint;
import com.zy.zlog.ZLog;

import java.util.ArrayList;
import java.util.List;

//拖动
public class DIYSortView extends View {


    public DIYSortView(Context context) {
        super(context);
        init(context);
    }


    public DIYSortView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public DIYSortView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    public DIYSortView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context);
    }

    Paint textPaint;
    Paint indexPaint;
    Paint assistLinePaint;
    Paint assistLinePaintV2;

    private void init(Context context) {

        textPaint = new Paint();
        //设置文本大小
        textPaint.setTextSize(72);
        textPaint.setAntiAlias(true);
        //设置文本对齐方式
        textPaint.setTextAlign(Paint.Align.CENTER);
        textPaint.setColor(Color.RED);

        indexPaint = new Paint();
        //设置文本大小
        indexPaint.setTextSize(36);
        indexPaint.setStyle(Paint.Style.STROKE);
        indexPaint.setStrokeWidth(5);
        //设置文本对齐方式
        indexPaint.setTextAlign(Paint.Align.CENTER);
        indexPaint.setColor(Color.BLUE);


        assistLinePaint = new Paint();
        assistLinePaint.setColor(Color.GRAY);
        DashPathEffect effects = new DashPathEffect(new float[]{20, 10, 20, 10}, 10);
        assistLinePaint.setPathEffect(effects);

        assistLinePaintV2 = new Paint();
        assistLinePaintV2.setColor(Color.BLACK);
        assistLinePaintV2.setStrokeWidth(4);
        assistLinePaintV2.setPathEffect(effects);


    }

    //参与运算(可以移动)
    private final List<Point> pointList = new ArrayList<>();
    //辅助node, 静态添加不移动
    private final List<Point> assistNodeList = new ArrayList<>();
    private final List<PointLine> assistLiseList = new ArrayList<>();

    int D = 0; //辅助格子大小
    int PER = 0; //每个node大小 一般小于 d/2

    public void setD(int d) {
        this.D = d;
        PER = d / 2 - 5;
    }


    /**
     * @param x    x位置
     * @param y    y位置
     * @param size 数据
     */
    public void create(int x, int y, int size, Point.Type type) {
        int[] array = DataBuildUtils.getRandomSortArray(size, size);
        pointList.addAll(CanvasUtils.buildFirst(x * D, y * D, D, array, PER, type));
    }

    public void create(int x, int y, int[] array, Point.Type type) {
        pointList.addAll(CanvasUtils.buildFirst(x * D, y * D, D, array, PER, type));
    }

    //添加辅助下标
    public void addAss(int x, int y, String v) {
        Point point = new Point.Builder().setOriginX(x * D).setOriginY(y * D).setV(v).setPer(PER).setIndex(0).build(Point.Type.SUB_SCRIPT);
        pointList.add(point);
    }

    //添加辅助元素
    public void addAssistList(int x, int y, int[] array, Point.Type type) {
        assistNodeList.addAll(CanvasUtils.buildAssistList(x * D, y * D, D, array, PER, type));
    }

    public void addAssistNode(int x, int y, String v) {
        Point point = new Point.Builder().setOriginX(x * D).setOriginY(y * D).setV(v).setPer(PER).setIndex(0).build(Point.Type.ASSIST);
        assistNodeList.add(point);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (D > 0) {
            //CanvasUtils.drawAssistLine(canvas, assistLinePaint, D);
            CanvasUtils.drawAssistLineV2(canvas, assistLinePaint, D);
            //辅助线
            for (PointLine line : assistLiseList) {
                line.draw(canvas, assistLinePaintV2);
            }
        }

        for (Point point : assistNodeList) {
            point.draw(canvas, textPaint, indexPaint);
        }

        for (Point point : pointList) {
            point.draw(canvas, textPaint, indexPaint);
        }
    }

    //保存node存在的位置
    //二维数组 [0行或者 0列 不保存数据]
    //node移动后 按照就近原则复原


    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        return super.dispatchTouchEvent(event);
    }

    long touchDownTime = 0;
    boolean hasMove = false; //是否移动过

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        ZLog.d("DIYSortView", "onTouchEvent:" + MotionEvent.actionToString(event.getAction()));
        //return super.onTouchEvent(event);

        int x = (int) event.getX();
        int y = (int) event.getY();

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                //找到当前位置的node，记录下来 (有可能没有node 不做任何事)
                curPoint = findNode(event);
                if (curPoint == null) {
                    return false;
                }
                downX = x;
                downY = y;
                touchDownTime = System.currentTimeMillis();
                hasMove = false;
                break;
            case MotionEvent.ACTION_MOVE:
                //移动距离阈值
                if (Math.abs(x - downX) > 50 || Math.abs(y - downY) > 50) {
                    //移动node
                    if (curPoint != null) {
                        hasMove = true;
                        curPoint.updateOff(x - downX, y - downY);
                        invalidate();
                    }
                }
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                //给node找到新的位置
                //新位置已经存在
                //新位置为kong
                //动画
                if (System.currentTimeMillis() - touchDownTime < 100) {
                    if (curPoint != null) {
                        curPoint.setSorted();
                        invalidate();
                    }
                }
                if (hasMove) {
                    moveUp(event);
                }
                break;
        }
        return true;
    }

    Point curPoint = null;
    int downX = 0;
    int downY = 0;

    private Point findNode(MotionEvent event) {
        for (Point point : pointList) {
            if (point.isRange(event) && point != curPoint) {
                return point;
            }
        }
        return null;
    }

    public void moveUp(MotionEvent event) {
        if (curPoint == null) {
            return;
        }
        int curX = (int) event.getX();
        int curY = (int) event.getY();


        //计算就近位置
        int targetX = (curX / D * D);

        if (curX - targetX > (D >> 1)) {
            targetX += D;
        }

        int targetY = (curY / D * D);
        if (curY - targetY > (D >> 1)) {
            targetY += D;
        }

        ZLog.d("DIYSortView", "moveUp:" + curX + " " + targetX + " " + curY + " " + targetY);

        Point findUpNode = findNode(event);
        ZLog.d("DIYSortView", "curPoint:" + curPoint + " findUpNode:" + findUpNode);
        if (findUpNode != null && curPoint != findUpNode) {
            //这种情况 node 交换
            changeNode(findUpNode, curPoint);
        }

        curPoint.updateOff(curX - targetX, curY - targetY);
        curPoint.setPercentage(1f);
        curPoint.setPoint(targetX, targetY);

        animatorNode(curPoint, 100);
    }


    //findUpNode -> curPoint
    private void changeNode(final Point findUpNode, Point curPoint) {
        float targetX = curPoint.getOriginX();
        float targetY = curPoint.getOriginY();

        float curX = findUpNode.getOriginX();
        float curY = findUpNode.getOriginY();

        findUpNode.updateOff(curX - targetX, curY - targetY);
        findUpNode.setPercentage(1f);
        findUpNode.setPoint(targetX, targetY);
        findUpNode.exchangeIndex(curPoint);

        animatorNode(findUpNode, 100);
    }

    private void animatorNode(final Point node, int duration) {
        animatorNode(node, duration, null);
    }

    private void animatorNode(final Point node, int duration, AnimatorListenerAdapter listenerAdapter) {
        ValueAnimator mAnimator = ValueAnimator.ofFloat(1, 0);
        mAnimator.setDuration(duration);
        mAnimator.setInterpolator(new LinearInterpolator());
        mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                //时间流,必须是线性的
                float percentage = (float) animation.getAnimatedValue();
                node.setPercentage(percentage);
                invalidate();
            }
        });

        mAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation, boolean isReverse) {
                super.onAnimationEnd(animation, isReverse);
                node.resetOff();
                if (listenerAdapter != null) {
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                        listenerAdapter.onAnimationEnd(animation, isReverse);
                    }
                }
            }
        });
        mAnimator.start();
    }


    private void changeNode(final Point node, float targetX, float targetY, AnimatorListenerAdapter listenerAdapter) {
        float curX = node.getOriginX();
        float curY = node.getOriginY();

        node.updateOff(curX - targetX, curY - targetY);
        node.setPercentage(1f);
        node.setPoint(targetX, targetY);
        animatorNode(node, 500, listenerAdapter);
    }


    //Node移动逻辑类
    public static class AutoModeStep {
        int index;
        int x;
        int y;

        public AutoModeStep(int index, int x, int y) {
            this.index = index;
            this.x = x;
            this.y = y;
        }
    }

    public void autoMove(List<AutoModeStep> list) {
        autoMove(list, 0);
    }

    private void autoMove(List<AutoModeStep> list, int index) {
        if (index >= list.size()) {
            return;
        }
        AutoModeStep step = list.get(index);
        Point point = findNodeByIndex(step.index);
        if (point == null) {
            throw new NullPointerException("autoMove Index is illegality");
        }
        changeNode(point, step.x * D, step.y * D, new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation, boolean isReverse) {
                super.onAnimationEnd(animation, isReverse);
                autoMove(list, index + 1);
            }
        });
    }


    private Point findNodeByIndex(int index) {
        for (Point point : pointList) {
            if (point.getIndex() == index) {
                return point;
            }
        }
        return null;
    }

    public static class NodeLineStep {
        int indexStart;
        int indexEnd;

        public NodeLineStep(int indexStart, int indexEnd) {
            this.indexStart = indexStart;
            this.indexEnd = indexEnd;
        }
    }

    public void autoAddAssistLine(List<NodeLineStep> list) {
        autoAddAssistLine(list, 0);
    }

    public void autoAddAssistLine(List<NodeLineStep> list, int index) {
        if (index >= list.size()) {
            return;
        }
        NodeLineStep nodeLineStep = list.get(index);
        Point start = findNodeByIndex(nodeLineStep.indexStart);
        Point end = findNodeByIndex(nodeLineStep.indexEnd);

        if (start == null || end == null) {
            throw new NullPointerException("autoAddAssistLine Index is illegality");
        }

        PointLine line = new PointLine(start.getOriginX(), start.getOriginY(), end.getOriginX(), end.getOriginY());
        assistLiseList.add(line);
        invalidate();


        ValueAnimator mAnimator = ValueAnimator.ofFloat(1, 0);
        mAnimator.setDuration(300);
        mAnimator.setInterpolator(new LinearInterpolator());
        mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                //时间流,必须是线性的
                float percentage = (float) animation.getAnimatedValue();
            }
        });

        mAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation, boolean isReverse) {
                super.onAnimationEnd(animation, isReverse);
                autoAddAssistLine(list, index + 1);
            }
        });
        mAnimator.start();
    }
}
